// -*- mode:c++ -*-

// Copyright (c) 2010-2012, 2014 ARM Limited
// All rights reserved
//
// The license below extends only to copyright in the software and shall
// not be construed as granting a license to any other intellectual
// property including but not limited to intellectual property relating
// to a hardware implementation of the functionality of the software
// licensed hereunder.  You may use the software subject to the license
// terms below provided that you ensure that this notice is replicated
// unmodified and in its entirety in all distributions of the software,
// modified or unmodified, in source code or in binary form.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met: redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer;
// redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution;
// neither the name of the copyright holders nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

let {{

    header_output = ""
    decoder_output = ""
    exec_output = ""

    # B, BL
    for (mnem, link) in (("b", False), ("bl", True)):
        bCode = '''
        NPC = (uint32_t)(PC + imm);
        '''
        br_tgt_code = '''pcs.instNPC((uint32_t)(pcs.instPC() + imm));'''
        instFlags = ["IsDirectControl"]
        if (link):
            bCode += '''
                if (Thumb)
                    LR = PC | 1;
                else
                    LR = PC - 4;
            '''
            instFlags += ["IsCall"]


        bIop = ArmInstObjParams(mnem, mnem.capitalize(), "BranchImmCond",
                { "code" : bCode, "predicate_test" : predicateTest,
                  "brTgtCode" : br_tgt_code }, instFlags)
        header_output += BranchImmCondDeclare.subst(bIop)
        decoder_output += BranchImmCondConstructor.subst(bIop) + \
                       BranchTarget.subst(bIop)
        exec_output += PredOpExecute.subst(bIop)

    # BX, BLX
    blxCode = '''
    %(link)s
    // Switch modes
    %(branch)s
    '''

    blxList = (("blx", True, True),
               ("blx", False, True),
               ("bx", False, False))

    for (mnem, imm, link) in blxList:
        Name = mnem.capitalize()
        isRasPop = 0
        if imm:
            Name += "Imm"
            # Since we're switching ISAs, the target ISA will be the opposite
            # of the current ISA. Thumb is whether the target is ARM.
            newPC = '(uint32_t)(Thumb ? (roundDown(PC, 4) + imm) : (PC + imm))'
            br_tgt_code = '''
            pcs.instNPC((uint32_t)(pcs.thumb() ? (roundDown(pcs.instPC(), 4) +
                            imm) : (pcs.instPC() + imm)));
            '''
            base = "BranchImmCond"
            declare = BranchImmCondDeclare
            constructor = BranchImmCondConstructor
            instFlags = ["IsDirectControl"]
        else:
            Name += "Reg"
            newPC = 'Op1'
            br_tgt_code = ''
            base = "BranchRegCond"
            declare = BranchRegCondDeclare
            constructor = BranchRegCondConstructor
            instFlags = ["IsIndirectControl"]
        if link and imm:
            linkStr = '''
                // The immediate version of the blx thumb instruction
                // is 32 bits wide, but "next pc" doesn't reflect that
                // so we don't want to substract 2 from it at this point
                if (Thumb)
                    LR = PC  | 1;
                else
                    LR = PC - 4;
            '''
            instFlags += ["IsCall"]
        elif link:
            linkStr = '''
                if (Thumb)
                    LR = (PC - 2) | 1;
                else
                    LR = PC - 4;
            '''
            instFlags += ["IsCall"]
        else:
            linkStr = ""
            isRasPop = "op1 == int_reg::Lr"

        if imm and link: #blx with imm
            branchStr = '''
                NextThumb = !Thumb;
                NPC = %(newPC)s;
            '''
            br_tgt_code = '''pcs.nextThumb(!pcs.thumb());\n''' + br_tgt_code
        else:
            branchStr = "IWNPC = %(newPC)s;"
        branchStr = branchStr % { "newPC" : newPC }

        code = blxCode % {"link": linkStr,
                          "newPC": newPC,
                          "branch": branchStr}
        blxIop = ArmInstObjParams(mnem, Name, base,
                { "code" : code, "brTgtCode" : br_tgt_code,
                  "predicate_test": predicateTest,
                  "is_ras_pop" : isRasPop }, instFlags)
        header_output += declare.subst(blxIop)
        decoder_output += constructor.subst(blxIop)
        exec_output += PredOpExecute.subst(blxIop)
        if imm:
            decoder_output += BranchTarget.subst(blxIop)

    bxjcode = '''
    HSTR hstr = Hstr;
    CPSR cpsr = Cpsr;

    if (ArmSystem::haveEL(xc->tcBase(), EL2) && hstr.tjdbx &&
        !isSecure(xc->tcBase()) && (cpsr.mode != MODE_HYP)) {
       fault = std::make_shared<HypervisorTrap>(
            machInst, op1, ExceptionClass::TRAPPED_BXJ);
    }
    IWNPC = Op1;
    '''

    bxjIop = ArmInstObjParams("bxj", "BxjReg", "BranchRegCond",
                              { "code": bxjcode,
                                "predicate_test": predicateTest,
                                "is_ras_pop": "op1 == int_reg::Lr" },
                              ["IsIndirectControl"])
    header_output += BranchRegCondDeclare.subst(bxjIop)
    decoder_output += BranchRegCondConstructor.subst(bxjIop)
    exec_output += PredOpExecute.subst(bxjIop)

    #CBNZ, CBZ. These are always unconditional as far as predicates
    for (mnem, test) in (("cbz", "=="), ("cbnz", "!=")):
        code = 'NPC = (uint32_t)(PC + imm);\n'
        br_tgt_code = '''pcs.instNPC((uint32_t)(pcs.instPC() + imm));'''
        predTest = "Op1 %(test)s 0" % {"test": test}
        iop = ArmInstObjParams(mnem, mnem.capitalize(), "BranchImmReg",
                               { "code" : code, "predicate_test" : predTest,
                                 "brTgtCode" : br_tgt_code},
                               ["IsDirectControl"])
        header_output += BranchImmRegDeclare.subst(iop)
        decoder_output += BranchImmRegConstructor.subst(iop) + \
                          BranchTarget.subst(iop)
        exec_output += PredOpExecute.subst(iop)

    #TBB, TBH
    for isTbh in (0, 1):
        if isTbh:
            eaCode = '''
            unsigned memAccessFlags = ArmISA::MMU::AllowUnaligned |
                                      ArmISA::MMU::AlignHalfWord;
            EA = Op1 + Op2 * 2
            '''
            accCode = 'NPC = PC + 2 * (Mem_uh);\n'
            mnem = "tbh"
        else:
            eaCode = '''
            unsigned memAccessFlags = ArmISA::MMU::AllowUnaligned |
                                      ArmISA::MMU::AlignByte;
            EA = Op1 + Op2
            '''
            accCode = 'NPC = PC + 2 * (Mem_ub)'
            mnem = "tbb"
        iop = ArmInstObjParams(mnem, mnem.capitalize(), "BranchRegReg",
                               { 'ea_code' : eaCode,
                                 'memacc_code' : accCode,
                                 'predicate_test' : predicateTest},
                               ["IsIndirectControl"])
        header_output += BranchTableDeclare.subst(iop)
        decoder_output += BranchRegRegConstructor.subst(iop)
        exec_output += LoadExecute.subst(iop) + \
                       LoadInitiateAcc.subst(iop) + \
                       LoadCompleteAcc.subst(iop)
}};
